{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "5hIbr52I7Z7U"
   },
   "source": [
    "Deep Learning\n",
    "=============\n",
    "\n",
    "Assignment 1\n",
    "------------\n",
    "\n",
    "The objective of this assignment is to learn about simple data curation practices, and familiarize you with some of the data we'll be reusing later.\n",
    "\n",
    "This notebook uses the [notMNIST](http://yaroslavvb.blogspot.com/2011/09/notmnist-dataset.html) dataset to be used with python experiments. This dataset is designed to look like the classic [MNIST](http://yann.lecun.com/exdb/mnist/) dataset, while looking a little more like real data: it's a harder task, and the data is a lot less 'clean' than MNIST."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "cellView": "both",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "collapsed": true,
    "id": "apJbCsBHl-2A"
   },
   "outputs": [],
   "source": [
    "# These are all the modules we'll be using later. Make sure you can import them\n",
    "# before proceeding further.\n",
    "from __future__ import print_function\n",
    "import imageio\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import os\n",
    "import sys\n",
    "import tarfile\n",
    "from IPython.display import display, Image\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from six.moves.urllib.request import urlretrieve\n",
    "from six.moves import cPickle as pickle\n",
    "\n",
    "# Config the matplotlib backend as plotting inline in IPython\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "jNWGtZaXn-5j"
   },
   "source": [
    "First, we'll download the dataset to our local machine. The data consists of characters rendered in a variety of fonts on a 28x28 image. The labels are limited to 'A' through 'J' (10 classes). The training set has about 500k and the testset 19000 labeled examples. Given these sizes, it should be possible to train models quickly on any machine."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "cellView": "both",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 186058,
     "status": "ok",
     "timestamp": 1444485672507,
     "user": {
      "color": "#1FA15D",
      "displayName": "Vincent Vanhoucke",
      "isAnonymous": false,
      "isMe": true,
      "permissionId": "05076109866853157986",
      "photoUrl": "//lh6.googleusercontent.com/-cCJa7dTDcgQ/AAAAAAAAAAI/AAAAAAAACgw/r2EZ_8oYer4/s50-c-k-no/photo.jpg",
      "sessionId": "2a0a5e044bb03b66",
      "userId": "102167687554210253930"
     },
     "user_tz": 420
    },
    "id": "EYRJ4ICW6-da",
    "outputId": "0d0f85df-155f-4a89-8e7e-ee32df36ec8d"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found and verified ./notMNIST_large.tar.gz\n",
      "Found and verified ./notMNIST_small.tar.gz\n"
     ]
    }
   ],
   "source": [
    "url = 'https://commondatastorage.googleapis.com/books1000/'\n",
    "last_percent_reported = None\n",
    "data_root = '.' # Change me to store data elsewhere\n",
    "\n",
    "def download_progress_hook(count, blockSize, totalSize):\n",
    "  \"\"\"A hook to report the progress of a download. This is mostly intended for users with\n",
    "  slow internet connections. Reports every 5% change in download progress.\n",
    "  \"\"\"\n",
    "  global last_percent_reported\n",
    "  percent = int(count * blockSize * 100 / totalSize)\n",
    "\n",
    "  if last_percent_reported != percent:\n",
    "    if percent % 5 == 0:\n",
    "      sys.stdout.write(\"%s%%\" % percent)\n",
    "      sys.stdout.flush()\n",
    "    else:\n",
    "      sys.stdout.write(\".\")\n",
    "      sys.stdout.flush()\n",
    "      \n",
    "    last_percent_reported = percent\n",
    "        \n",
    "def maybe_download(filename, expected_bytes, force=False):\n",
    "  \"\"\"Download a file if not present, and make sure it's the right size.\"\"\"\n",
    "  dest_filename = os.path.join(data_root, filename)\n",
    "  if force or not os.path.exists(dest_filename):\n",
    "    print('Attempting to download:', filename) \n",
    "    filename, _ = urlretrieve(url + filename, dest_filename, reporthook=download_progress_hook)\n",
    "    print('\\nDownload Complete!')\n",
    "  statinfo = os.stat(dest_filename)\n",
    "  if statinfo.st_size == expected_bytes:\n",
    "    print('Found and verified', dest_filename)\n",
    "  else:\n",
    "    raise Exception(\n",
    "      'Failed to verify ' + dest_filename + '. Can you get to it with a browser?')\n",
    "  return dest_filename\n",
    "\n",
    "train_filename = maybe_download('notMNIST_large.tar.gz', 247336696)\n",
    "test_filename = maybe_download('notMNIST_small.tar.gz', 8458043)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "cC3p0oEyF8QT"
   },
   "source": [
    "Extract the dataset from the compressed .tar.gz file.\n",
    "This should give you a set of directories, labeled A through J."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "cellView": "both",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 186055,
     "status": "ok",
     "timestamp": 1444485672525,
     "user": {
      "color": "#1FA15D",
      "displayName": "Vincent Vanhoucke",
      "isAnonymous": false,
      "isMe": true,
      "permissionId": "05076109866853157986",
      "photoUrl": "//lh6.googleusercontent.com/-cCJa7dTDcgQ/AAAAAAAAAAI/AAAAAAAACgw/r2EZ_8oYer4/s50-c-k-no/photo.jpg",
      "sessionId": "2a0a5e044bb03b66",
      "userId": "102167687554210253930"
     },
     "user_tz": 420
    },
    "id": "H8CBE-WZ8nmj",
    "outputId": "ef6c790c-2513-4b09-962e-27c79390c762"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./notMNIST_large already present - Skipping extraction of ./notMNIST_large.tar.gz.\n",
      "['./notMNIST_large/A', './notMNIST_large/B', './notMNIST_large/C', './notMNIST_large/D', './notMNIST_large/E', './notMNIST_large/F', './notMNIST_large/G', './notMNIST_large/H', './notMNIST_large/I', './notMNIST_large/J']\n",
      "./notMNIST_small already present - Skipping extraction of ./notMNIST_small.tar.gz.\n",
      "['./notMNIST_small/A', './notMNIST_small/B', './notMNIST_small/C', './notMNIST_small/D', './notMNIST_small/E', './notMNIST_small/F', './notMNIST_small/G', './notMNIST_small/H', './notMNIST_small/I', './notMNIST_small/J']\n"
     ]
    }
   ],
   "source": [
    "num_classes = 10\n",
    "np.random.seed(133)\n",
    "\n",
    "def maybe_extract(filename, force=False):\n",
    "  root = os.path.splitext(os.path.splitext(filename)[0])[0]  # remove .tar.gz\n",
    "  if os.path.isdir(root) and not force:\n",
    "    # You may override by setting force=True.\n",
    "    print('%s already present - Skipping extraction of %s.' % (root, filename))\n",
    "  else:\n",
    "    print('Extracting data for %s. This may take a while. Please wait.' % root)\n",
    "    tar = tarfile.open(filename)\n",
    "    sys.stdout.flush()\n",
    "    tar.extractall(data_root)\n",
    "    tar.close()\n",
    "  data_folders = [\n",
    "    os.path.join(root, d) for d in sorted(os.listdir(root))\n",
    "    if os.path.isdir(os.path.join(root, d))]\n",
    "  if len(data_folders) != num_classes:\n",
    "    raise Exception(\n",
    "      'Expected %d folders, one per class. Found %d instead.' % (\n",
    "        num_classes, len(data_folders)))\n",
    "  print(data_folders)\n",
    "  return data_folders\n",
    "  \n",
    "train_folders = maybe_extract(train_filename)\n",
    "test_folders = maybe_extract(test_filename)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "4riXK3IoHgx6"
   },
   "source": [
    "---\n",
    "Problem 1\n",
    "---------\n",
    "\n",
    "Let's take a peek at some of the data to make sure it looks sensible. Each exemplar should be an image of a character A through J rendered in a different font. Display a sample of the images that we just downloaded. Hint: you can use the package IPython.display.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAAAAABXZoBIAAAB0ElEQVR4nG3SO2uUQRjF8f/MOxuX\ngKyQiBAwxE5B8FKJnZcPENAiKMJaiKAfQCsLFRRriQTcgKBs4QUEFRGDYBMRxY3RIpomKEgSlV2M\nYfPO5Vi8ibquTzm/Ys48c6BrjOXUbOvN8aybwFJTVNLEULc5LspHRa+H/7GqgiRFTXXbgVypwKe2\n07KwvV5KRgIx14lZHLy/MdokA/Cx09j0VkFRrSUpaqTT+l/JK+rTrucK0v6O9/UVtriDB/Ja2f23\nVSblldTeBzXlWtr2O5BN5bt7glPi5LMyTSCurJkx9p5ypaAzuBIXlOv75jWz3JBXCjrHOuu4rFzf\nBlYtY1ReijqLhfVDj+Q137dmV+WlpCuVg9VLjz9HKWmuDGAzrslLUruxLElSUlTDANZyvTBJit77\nmKSgJziMzM2jwRWXR4TJAMRXnCGrHw4OUJItzpdNL9DEyd0eDg6CtRn8mPnw7v2XmZGxCD9xjA/7\nEpJj4fXky6l5gQv9CNq46rFYQjJj9UarqAKG1ZW7IxiEOVGzyVolBYzMFmyiF7cBkD1d60lKiSKn\ncpIoY6eJ3t4adXnQn0+/g7FMw9YFaXGQzi6Z8800UQH2vpg9xD/Vtwzs7IFf8DcdSRkFdyUAAAAA\nSUVORK5CYII=\n",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import display, Image\n",
    "\n",
    "\n",
    "display(Image(filename=\"notMNIST_small/A/Q0NXaWxkV29yZHMtQm9sZEl0YWxpYy50dGY=.png\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "PBdkjESPK8tw"
   },
   "source": [
    "Now let's load the data in a more manageable format. Since, depending on your computer setup you might not be able to fit it all in memory, we'll load each class into a separate dataset, store them on disk and curate them independently. Later we'll merge them into a single dataset of manageable size.\n",
    "\n",
    "We'll convert the entire dataset into a 3D array (image index, x, y) of floating point values, normalized to have approximately zero mean and standard deviation ~0.5 to make training easier down the road. \n",
    "\n",
    "A few images might not be readable, we'll just skip them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "cellView": "both",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "output_extras": [
      {
       "item_id": 30
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 399874,
     "status": "ok",
     "timestamp": 1444485886378,
     "user": {
      "color": "#1FA15D",
      "displayName": "Vincent Vanhoucke",
      "isAnonymous": false,
      "isMe": true,
      "permissionId": "05076109866853157986",
      "photoUrl": "//lh6.googleusercontent.com/-cCJa7dTDcgQ/AAAAAAAAAAI/AAAAAAAACgw/r2EZ_8oYer4/s50-c-k-no/photo.jpg",
      "sessionId": "2a0a5e044bb03b66",
      "userId": "102167687554210253930"
     },
     "user_tz": 420
    },
    "id": "h7q0XhG3MJdf",
    "outputId": "92c391bb-86ff-431d-9ada-315568a19e59"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./notMNIST_large/A.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/B.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/C.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/D.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/E.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/F.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/G.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/H.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/I.pickle already present - Skipping pickling.\n",
      "./notMNIST_large/J.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/A.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/B.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/C.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/D.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/E.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/F.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/G.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/H.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/I.pickle already present - Skipping pickling.\n",
      "./notMNIST_small/J.pickle already present - Skipping pickling.\n"
     ]
    }
   ],
   "source": [
    "image_size = 28  # Pixel width and height.\n",
    "pixel_depth = 255.0  # Number of levels per pixel.\n",
    "\n",
    "def load_letter(folder, min_num_images):\n",
    "  \"\"\"Load the data for a single letter label.\"\"\"\n",
    "  image_files = os.listdir(folder)\n",
    "  dataset = np.ndarray(shape=(len(image_files), image_size, image_size),\n",
    "                         dtype=np.float32)\n",
    "  print(folder)\n",
    "  num_images = 0\n",
    "  for image in image_files:\n",
    "    image_file = os.path.join(folder, image)\n",
    "    try:\n",
    "      image_data = (imageio.imread(image_file).astype(float) - \n",
    "                    pixel_depth / 2) / pixel_depth\n",
    "      if image_data.shape != (image_size, image_size):\n",
    "        raise Exception('Unexpected image shape: %s' % str(image_data.shape))\n",
    "      dataset[num_images, :, :] = image_data\n",
    "      num_images = num_images + 1\n",
    "    except (IOError, ValueError) as e:\n",
    "      print('Could not read:', image_file, ':', e, '- it\\'s ok, skipping.')\n",
    "    \n",
    "  dataset = dataset[0:num_images, :, :]\n",
    "  if num_images < min_num_images:\n",
    "    raise Exception('Many fewer images than expected: %d < %d' %\n",
    "                    (num_images, min_num_images))\n",
    "    \n",
    "  print('Full dataset tensor:', dataset.shape)\n",
    "  print('Mean:', np.mean(dataset))\n",
    "  print('Standard deviation:', np.std(dataset))\n",
    "  return dataset\n",
    "        \n",
    "def maybe_pickle(data_folders, min_num_images_per_class, force=False):\n",
    "  dataset_names = []\n",
    "  for folder in data_folders:\n",
    "    set_filename = folder + '.pickle'\n",
    "    dataset_names.append(set_filename)\n",
    "    if os.path.exists(set_filename) and not force:\n",
    "      # You may override by setting force=True.\n",
    "      print('%s already present - Skipping pickling.' % set_filename)\n",
    "    else:\n",
    "      print('Pickling %s.' % set_filename)\n",
    "      dataset = load_letter(folder, min_num_images_per_class)\n",
    "      try:\n",
    "        with open(set_filename, 'wb') as f:\n",
    "          pickle.dump(dataset, f, pickle.HIGHEST_PROTOCOL)\n",
    "      except Exception as e:\n",
    "        print('Unable to save data to', set_filename, ':', e)\n",
    "  \n",
    "  return dataset_names\n",
    "\n",
    "train_datasets = maybe_pickle(train_folders, 45000)\n",
    "test_datasets = maybe_pickle(test_folders, 1800)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "vUdbskYE2d87"
   },
   "source": [
    "---\n",
    "Problem 2\n",
    "---------\n",
    "\n",
    "Let's verify that the data still looks good. Displaying a sample of the labels and images from the ndarray. Hint: you can use matplotlib.pyplot.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nGwAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f5329f2ac50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pickle_file = train_datasets[5]  # index 0 should be all As, 1 = all Bs, etc.\n",
    "with open(pickle_file, 'rb') as f:\n",
    "    letter_set = pickle.load(f)  # unpickle\n",
    "    print(len(letter_set))\n",
    "    sample_idx = np.random.randint(len(letter_set))  # pick a random image index\n",
    "    sample_image = letter_set[sample_idx, :, :]  # extract a 2D slice\n",
    "    plt.figure()\n",
    "    plt.imshow(sample_image)  # display it"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "cYznx5jUwzoO"
   },
   "source": [
    "---\n",
    "Problem 3\n",
    "---------\n",
    "Another check: we expect the data to be balanced across classes. Verify that.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "LA7M7K22ynCt"
   },
   "source": [
    "Merge and prune the training data as needed. Depending on your computer setup, you might not be able to fit it all in memory, and you can tune `train_size` as needed. The labels will be stored into a separate array of integers 0 through 9.\n",
    "\n",
    "Also create a validation dataset for hyperparameter tuning."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "both",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 411281,
     "status": "ok",
     "timestamp": 1444485897869,
     "user": {
      "color": "#1FA15D",
      "displayName": "Vincent Vanhoucke",
      "isAnonymous": false,
      "isMe": true,
      "permissionId": "05076109866853157986",
      "photoUrl": "//lh6.googleusercontent.com/-cCJa7dTDcgQ/AAAAAAAAAAI/AAAAAAAACgw/r2EZ_8oYer4/s50-c-k-no/photo.jpg",
      "sessionId": "2a0a5e044bb03b66",
      "userId": "102167687554210253930"
     },
     "user_tz": 420
    },
    "id": "s3mWgZLpyuzq",
    "outputId": "8af66da6-902d-4719-bedc-7c9fb7ae7948"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training (200000, 28, 28) (200000,)\n",
      "Validation (10000, 28, 28) (10000,)\n",
      "Testing (10000, 28, 28) (10000,)\n"
     ]
    }
   ],
   "source": [
    "def make_arrays(nb_rows, img_size):\n",
    "  if nb_rows:\n",
    "    dataset = np.ndarray((nb_rows, img_size, img_size), dtype=np.float32)\n",
    "    labels = np.ndarray(nb_rows, dtype=np.int32)\n",
    "  else:\n",
    "    dataset, labels = None, None\n",
    "  return dataset, labels\n",
    "\n",
    "def merge_datasets(pickle_files, train_size, valid_size=0):\n",
    "  num_classes = len(pickle_files)\n",
    "  valid_dataset, valid_labels = make_arrays(valid_size, image_size)\n",
    "  train_dataset, train_labels = make_arrays(train_size, image_size)\n",
    "  vsize_per_class = valid_size // num_classes\n",
    "  tsize_per_class = train_size // num_classes\n",
    "    \n",
    "  start_v, start_t = 0, 0\n",
    "  end_v, end_t = vsize_per_class, tsize_per_class\n",
    "  end_l = vsize_per_class+tsize_per_class\n",
    "  for label, pickle_file in enumerate(pickle_files):       \n",
    "    try:\n",
    "      with open(pickle_file, 'rb') as f:\n",
    "        letter_set = pickle.load(f)\n",
    "        # let's shuffle the letters to have random validation and training set\n",
    "        np.random.shuffle(letter_set)\n",
    "        if valid_dataset is not None:\n",
    "          valid_letter = letter_set[:vsize_per_class, :, :]\n",
    "          valid_dataset[start_v:end_v, :, :] = valid_letter\n",
    "          valid_labels[start_v:end_v] = label\n",
    "          start_v += vsize_per_class\n",
    "          end_v += vsize_per_class\n",
    "                    \n",
    "        train_letter = letter_set[vsize_per_class:end_l, :, :]\n",
    "        train_dataset[start_t:end_t, :, :] = train_letter\n",
    "        train_labels[start_t:end_t] = label\n",
    "        start_t += tsize_per_class\n",
    "        end_t += tsize_per_class\n",
    "    except Exception as e:\n",
    "      print('Unable to process data from', pickle_file, ':', e)\n",
    "      raise\n",
    "    \n",
    "  return valid_dataset, valid_labels, train_dataset, train_labels\n",
    "            \n",
    "            \n",
    "train_size = 200000\n",
    "valid_size = 10000\n",
    "test_size = 10000\n",
    "\n",
    "valid_dataset, valid_labels, train_dataset, train_labels = merge_datasets(\n",
    "  train_datasets, train_size, valid_size)\n",
    "_, _, test_dataset, test_labels = merge_datasets(test_datasets, test_size)\n",
    "\n",
    "print('Training:', train_dataset.shape, train_labels.shape)\n",
    "print('Validation:', valid_dataset.shape, valid_labels.shape)\n",
    "print('Testing:', test_dataset.shape, test_labels.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "GPTCnjIcyuKN"
   },
   "source": [
    "Next, we'll randomize the data. It's important to have the labels well shuffled for the training and test distributions to match."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "both",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "collapsed": true,
    "id": "6WZ2l2tN2zOL"
   },
   "outputs": [],
   "source": [
    "def randomize(dataset, labels):\n",
    "  permutation = np.random.permutation(labels.shape[0])\n",
    "  shuffled_dataset = dataset[permutation,:,:]\n",
    "  shuffled_labels = labels[permutation]\n",
    "  return shuffled_dataset, shuffled_labels\n",
    "train_dataset, train_labels = randomize(train_dataset, train_labels)\n",
    "test_dataset, test_labels = randomize(test_dataset, test_labels)\n",
    "valid_dataset, valid_labels = randomize(valid_dataset, valid_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "puDUTe6t6USl"
   },
   "source": [
    "---\n",
    "Problem 4\n",
    "---------\n",
    "Convince yourself that the data is still good after shuffling!\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "tIQJaJuwg5Hw"
   },
   "source": [
    "Finally, let's save the data for later reuse:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "both",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "collapsed": true,
    "id": "QiR_rETzem6C"
   },
   "outputs": [],
   "source": [
    "pickle_file = os.path.join(data_root, 'notMNIST.pickle')\n",
    "\n",
    "try:\n",
    "  f = open(pickle_file, 'wb')\n",
    "  save = {\n",
    "    'train_dataset': train_dataset,\n",
    "    'train_labels': train_labels,\n",
    "    'valid_dataset': valid_dataset,\n",
    "    'valid_labels': valid_labels,\n",
    "    'test_dataset': test_dataset,\n",
    "    'test_labels': test_labels,\n",
    "    }\n",
    "  pickle.dump(save, f, pickle.HIGHEST_PROTOCOL)\n",
    "  f.close()\n",
    "except Exception as e:\n",
    "  print('Unable to save data to', pickle_file, ':', e)\n",
    "  raise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "both",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 413065,
     "status": "ok",
     "timestamp": 1444485899688,
     "user": {
      "color": "#1FA15D",
      "displayName": "Vincent Vanhoucke",
      "isAnonymous": false,
      "isMe": true,
      "permissionId": "05076109866853157986",
      "photoUrl": "//lh6.googleusercontent.com/-cCJa7dTDcgQ/AAAAAAAAAAI/AAAAAAAACgw/r2EZ_8oYer4/s50-c-k-no/photo.jpg",
      "sessionId": "2a0a5e044bb03b66",
      "userId": "102167687554210253930"
     },
     "user_tz": 420
    },
    "id": "hQbLjrW_iT39",
    "outputId": "b440efc6-5ee1-4cbc-d02d-93db44ebd956"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Compressed pickle size: 718193801\n"
     ]
    }
   ],
   "source": [
    "statinfo = os.stat(pickle_file)\n",
    "print('Compressed pickle size:', statinfo.st_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "gE_cRAQB33lk"
   },
   "source": [
    "---\n",
    "Problem 5\n",
    "---------\n",
    "\n",
    "By construction, this dataset might contain a lot of overlapping samples, including training data that's also contained in the validation and test set! Overlap between training and test can skew the results if you expect to use your model in an environment where there is never an overlap, but are actually ok if you expect to see training samples recur when you use it.\n",
    "Measure how much overlap there is between training, validation and test samples.\n",
    "\n",
    "Optional questions:\n",
    "- What about near duplicates between datasets? (images that are almost identical)\n",
    "- Create a sanitized validation and test set, and compare your accuracy on those in subsequent assignments.\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "L8oww1s4JMQx"
   },
   "source": [
    "---\n",
    "Problem 6\n",
    "---------\n",
    "\n",
    "Let's get an idea of what an off-the-shelf classifier can give you on this data. It's always good to check that there is something to learn, and that it's a problem that is not so trivial that a canned solution solves it.\n",
    "\n",
    "Train a simple model on this data using 50, 100, 1000 and 5000 training samples. Hint: you can use the LogisticRegression model from sklearn.linear_model.\n",
    "\n",
    "Optional question: train an off-the-shelf model on all the data!\n",
    "\n",
    "---"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "default_view": {},
   "name": "1_notmnist.ipynb",
   "provenance": [],
   "version": "0.3.2",
   "views": {}
  },
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
